package com.dh.convert.node;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import com.dh.convert.constants.Constants;
import com.dh.convert.jackson.JSON;
import com.dh.convert.mapping.BeanMappingObject;
import com.dh.convert.utils.ClassUtils;
import com.dh.convert.utils.JsonNodeUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.aviator.AviatorEvaluator;

public class ArrayNode extends ObjectNode {

	protected String srcListName;
	protected String srcPath;
	protected ObjectNode chlidNode;
	protected Class<?> entryClass;
	protected String entryType;
	protected boolean _entryIsCommon = Boolean.TRUE;

	public void setSrcListName(String srcListName) {
		this.srcListName = srcListName;
	}

	public String getSrcListName() {
		return srcListName;
	}

	public void setSrcPath(String srcPath) {
		this.srcPath = srcPath;
	}

	public String getSrcPath() {
		return srcPath;
	}

	@Override
	public TreeNode setEntryClass(Class<?> cls) {
		this.entryClass = cls;
		return this;
	}

	@Override
	public TreeNode bulid(Field f) {
		this.f = f;
		this.f.setAccessible(Boolean.TRUE);
		this._isArray = Boolean.TRUE;
		this.nodeName = f.getName();
		this.currentClass = f.getType();
		this.typeName = this.currentClass.getName();
		this.entryClass = getEntryClass(f);
		this.entryType = entryClass.getSimpleName();
		bulidEntryNode(entryClass);
		return this;
	}

	public TreeNode bulidMapEntry() {
		this.entryClass = LinkedHashMap.class;
		bulidEntryNode(entryClass);
		return this;
	}

	protected TreeNode bulidEntryNode(Class<?> cls) {
		if (cls!=null && !ClassUtils.isCommon(cls)) {
			bulidChlidNode(cls);
		}
		return this;
	}

	protected Class<?> getEntryClass(Field f) {
		return f.getType().getComponentType();
	}

	public void isCollectionMapping(boolean isCollectionMapping) {
		this._collectionMapping = isCollectionMapping;
	}

	@Override
	public Object genObject(JsonNode jn, Object targetObject, Object currentObject,Map<String, Object> context)
			throws InstantiationException, IllegalAccessException {
		Collection collection = null;
		if (_isRoot)
			collection = (Collection) targetObject;
		else if(currentObject!=null){
			collection = (Collection) currentObject;
		}else{
			collection = new ArrayList<>();
		}
		collection = genList(jn, targetObject, collection, context);
		if (CollectionUtils.isNotEmpty(collection))
			return collection.toArray();
		return null;
	}

	protected Collection genList(JsonNode jn, Object targetObject, Collection collection, Map<String, Object> context)
			throws InstantiationException, IllegalAccessException {
		int size = 1;
		JsonNode listJsonNode = null;
		List<?> srcList = null;
		Boolean hasEntry = Boolean.FALSE;
		if (_collectionMapping) {
			if (StringUtils.isNoneBlank(srcListName)) {
				srcList = (List<?>) AviatorEvaluator.execute(srcListName, context, Boolean.TRUE);
				if (CollectionUtils.isEmpty(srcList))
					return null;
				size = srcList.size();
			} else {
				listJsonNode = JsonNodeUtils.getJsonNode(jn, srcPath, null);
				if (listJsonNode == null)
					return null;
				size = listJsonNode.size();
			}
		}
		if(collection.size() == size)
			hasEntry = Boolean.TRUE;
		
		for (int i = 0; i < size; i++) {
			Object entry = null;
			if(hasEntry){
				entry = CollectionUtils.get(collection, i);
			}else if (!ClassUtils.isCommon(entryClass)) {
				entry = entryClass.newInstance();
			}
			Object srcEntry = null;
			if (listJsonNode != null) {
				srcEntry = JSON.treeToValue(listJsonNode.get(i), entryClass);
				context.put(Constants.CURRENT_LIST_ENTRY, srcEntry);
			} else if (CollectionUtils.isNotEmpty(srcList)){
				srcEntry = srcList.get(i);
				context.put(Constants.CURRENT_LIST_ENTRY, srcEntry);
			}

			if (MapUtils.isEmpty(mappingChildNodes) && srcEntry !=null && !hasEntry) {
				collection.add(srcEntry);
				continue;
			}
			Boolean isEmpty = Boolean.TRUE;
			for (Entry<TreeNode, BeanMappingObject> e : mappingChildNodes.entrySet()) {
				BeanMappingObject bmo = e.getValue();
				TreeNode currentNode = e.getKey();
				Object value = currentNode.getTargetValue(jn, bmo, targetObject, context, i);
				if (value == null)
					continue;
				isEmpty = Boolean.FALSE;
				if (entry instanceof Map) {
					((Map) entry).put(currentNode.getNodeName(), value);
				} else if (entry == null)
					collection.add(value);
				else
					currentNode.setValue(entry, value);
			}
			if (!hasEntry && entry != null && !isEmpty)
				collection.add(entry);
		}
		return collection;
	}

	@Override
	public String toString() {
		return " [ nodeName=" + nodeName + " type=" + typeName + " parentNode=" + parentNodeName + " entryType="
				+ entryType + " childNodes=" + childNodes + "]";
	}
}
